from django.db.models import Q
from django.shortcuts import render
from django.views import View
from django.http import JsonResponse
from django import http
from django.contrib.auth import login, authenticate, logout
from django_redis import get_redis_connection
from django.contrib.auth.mixins import LoginRequiredMixin
import re
import json
import logging

from users.models import User, Address
from .models import Address
from meiduo_mall.utils.secret import SecretOauth
from users.utils import generate_verify_email_url
from celery_tasks.email.tasks import send_verify_email

logger = logging.getLogger('django')
# Create your views here.

# 判断用户名重复注册接口
class UsernameCountView(View):

    # 由于接口定义的请求方式是GET，所以映射的视图方法是self.get()
    # 所以我们需要定义self.get()视图方法
    def get(self, request, username):
        # 1、提取参数
        # username是路径参数，在路由匹配的时候通过正则或者转化器提取传递到视图函数中。
        # 2、校验参数
        # 路径参数正则匹配或转化器匹配，已经起到了校验的作用。
        # 3、数据处理 —— 根据username统计用户数量
        try:
            count = User.objects.filter(
                username=username
            ).count()
        except Exception as e:
            print(e)
            # 记录日志
            logger.info("数据库访问失败！")
            # 构建错误响应
            return JsonResponse({
                "code": 400,
                "errmsg": "数据库错误"
            })

        # 4、构建响应
        return JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "count": count
        })



class MobileCountView(View):

    def get(self,request,mobile):
        """判断手机号是否重复注册"""
        #1.查询mobile在mysql中的个数

        try:
            count =User.objects.filter(mobile=mobile).count()

        except Exception as e:
            print(e)
            return http.JsonResponse({
                'code':400,
                'errmsg':'查询数据库出错'
            })

        #返回结果(json)
        return http.JsonResponse({
            'code':400,
            'errmsg':'ok',
            'count':count

        })



class RegisterView(View):


    def post(self, request, get_redis_conection=None):
        #q.提取参数
        #request.body-->b"{'username':'weiwei'}
        json_str =request.body.decode()#"{'username':'weiwei'}
        data = json.loads(json_str)#将JSON字符串,转成python的标准字典
        #json_dict = json.loads(request.body.decode()0

        #必传
        username =data.get('username')
        password = data.get('password')
        password2 = data.get('password2')
        mobile = data.get('mobile')
        sms_code =data.get('sms_code')
        allow =data.get('allow')

        #2.检验参数
        #2.1必要性检验
        if not all([username,password,mobile,sms_code,]):
            return  JsonResponse({
                'code':400,
                'errmsg':'用户格式错误'
            })
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',username):
            return http.JsonResponse({
                'code':400,
                'errmsg':'username格式错误'
            })

        if not re.match(r'^[a-zA-Z0-9]{8,20}$',password):
            return http.JsonResponse({
                'coede':400,
                'errmsg':'密码格式错误'
            })
        if password != password2:
            return http.JsonResponse({
                'code':400,
                'errmsg':'两次输入不对'
            })

        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return http.JsonResponse({
                'code':400,
                'errmsg':'手机号格式错误'

            })
        if allow != True:
            return http.JsonResponse({
                'code':400,
                'errmsg':'未同意协议'
            })






        #2.3\业务校验--短信验证码等
        #TODO:此处将来填充校验短信验证码逻辑代码
        #1.读取redis中记录的手机验证码
        #2. 判断是否过期
        #4.判断是否一致
            # 判断短信验证码是否正确:跟图形验证码的验证一样的逻辑
            # 提取服务端存储的短信验证码:怎么存,怎么提取
        redis_conn = get_redis_connection('verify_code')
        sms_code_server = redis_conn.get('sms_%s' % mobile)  # sms_code_server是bytes

        # 判断短信验证码是否过期
        if not sms_code_server:
            return http.JsonResponse({
                'code': 400,
                'errmsg': '短信验证码失效'
            })
        if sms_code != sms_code_server.decode():
            return http.JsonResponse({
                'code': 400,
                'errmsg': '验证码有误'
            })

        #3.业务数据处理---新建用户模型类对象保存数据库,用户状态保持.
        #create 密码不加密
        #create_user 密码才会加密 AbstractUser提供方法
        #create_superuser() --密码加密 超级管理员,要求必传邮箱
        #此处醒目新建的是普通用户,使用只需要使用create_user
        try:
            user = User.objects.create_user(
                username=username,
                password=password,
                mobile=mobile
            )
        except Exception as e:
            logger.error('注册用户,写入mysql失败!')
            print(e)
            return JsonResponse({
                'code':400,
                'errmsg':'服务器繁忙,请稍后'
            })
        #添加仅此一行代码
        # TODO 用户状态保持 --- 把用户数据写入session缓存用于下一次请求验证用户身份
        # 功能1.用户信息写入session缓存;2.在响应中添加session记录在cookie中
        # 实现状态保持
        login(request, user)
        return http.JsonResponse({
            'code':400,
            'errmsg':'注册成功'
        })

#用户登陆
class LoginView(View):

    def post(self,request):
    #1.提取参数
        data = json.loads(request.body.decode())
        username =data.get('username')
        password =data.get('password')
        remembered =data.get('remembered',False)

        #2.校验参数
        #2.1 必要性校验
        if not all([username,password]):
            return JsonResponse({'code':400,'errmsg':'缺少必要参数'})

        #约束性校验
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$',username):
            return JsonResponse({'code':400,'errmsg':'用户名格式错误'})
        if not re.match(r'^[a-zA-Z0-9]{8,20}$',password):
            return JsonResponse({
                'code':400,
                'errmsg':'密码格式错误'
            })

        #3.业务处理--根据用户填写的用户和密码进行校验
        #3.1根据用户名查询用户模型类对象
        # try:
        #     #get函数是根据字段过滤出"唯一"对象,找不到则跑出DoesNotExist
        #
        #     user = User.objects.get(
        #         Q(username=username)|Q(mobile=username)
        #     )
        #
        # except User.DoesNotExist as e:
        #     return JsonResponse({'code':400,'errmsg':'用户名或者密码错误'})
        #
        # #验证密码
        # if not user.check_password(password):
        #     return JsonResponse({'code':400,'errmsg':'用户名或者密码错误'})


        user = authenticate(request,username=username,password=password)
        if  user is None:
            return JsonResponse({'code': 400,'errmsg':'用户名或者密码错误'})


        #TODO:状态保持
        login(request,user)

        #TODO:判断是否记住用户
        if remembered:
        #用户记住了,长期保持
            request.session.set_expiry(None)#如果这是None,表示该用户session数据记录2周有效期
        else:
            #用户没勾选的话,环比浏览器页面后失效
            request.session.set_expiry(0)#设置为0表示立即失效
        #构建响应

        response=http.JsonResponse({'code':0,'errmsg':'ok'})

        #在响应对象中设置用户信息
        #将用户名写如cookie中,有效期14天
        response.set_cookie('username',user.username,max_age=3600*24*14)

        return response

#用户中心-->前提条件:只有用户登陆才能被访问
class UserInfoView(LoginRequiredMixin,View):

    def get(self,request):
    #判断用户是否已经登陆
    #=====如果积极登陆,是user模型对象======
    #request.user.is_authenticate=true
    #request.user.is_anonymous=false
    #======如果没有登陆,时anonymous匿名用户模型类对象===
    #request.user.is_authencicate =False
    #request.user.is_anmonymous =True


        #通过该user对象,来判断是否已经登陆

        #已经登陆
        info_data={
            'username':request.user.username,
            'mobile':request.user.mobile,
            'email':request.user.email,
            'email_active':request.user.email_active
            }

        #返回响应
        return JsonResponse({'code':0,'errmsg':'ok','info_data':info_data})

#退出登陆
class LogoutView(View):
    """实现退出登陆"""

    def delete(self,request):
        """实现退出登陆逻辑"""

        #清理session
        logout(request)

        #创建response对象
        response = http.JsonResponse({'code':0,'errmsg':'ok'})

        #调用对象的delete_cookie('username')
        response.delete_cookie('username')

        #构建响应
        return response


#添加邮箱


class EmailView(LoginRequiredMixin,View):
    """添加邮箱"""
    def put(self, request,):
        """实现添加邮箱"""
        data = json.loads(request.body.decode())
        email =data.get('email')
        #2.校验参数
        if not email:
            return JsonResponse({'code':400,'errmsg':'缺少参数'})
        if not re.match(r'^[a-zA-Z0-9]+@[a-zA-Z0-9_-]+(.\.[a-zA-Z0-9_-]+)+$',email):
            return JsonResponse({'code':400,'errmsg':'邮箱格式错误'})
        # 3. 业务处理——（1）.保存更新邮箱。（2）。发送验证邮箱（业务性校验）
        try:
            user = request.user#必须是User对象——必须登陆
            user.email =email
            user.save()
        except Exception as e:
            logger.error('数据库更新邮箱失败，错误信息：%s'% e)
            return JsonResponse({'code':400,'errmsg':'数据库更新失败'})

        #TODO：发送邮件
        verify_url = generate_verify_email_url(request)
        send_verify_email.delay(email,verify_url)

        #构建响应1

        return JsonResponse({'code':0,'errmsg':'ok'})



#激活邮箱
class VerifyEmailView(View):
    #激活邮箱，验证Token
    def put(self,request):
        #1.提取参数
        token =request.GET.get('token')
        #2.校验参数
        if not token:
            return JsonResponse({'code':400,'errmsg':'缺少参数'})
        #业务数据处理——校验token邮箱，激活邮箱
        user_info = SecretOauth().loads(token)
        if user_info is None:
            return JsonResponse({'code':400,'errmsg':'邮箱验证失败'})
        user_id = user_info.get('user_id')
        email =user_info.get('email')
        try:
            user =User.objects.get(id =user_id,email=email,is_active=True)
        except User.DoesNotExist as e:
            logger.info('邮箱验证用户不存在或已注销：%s'% e)
            return JsonResponse({'code':400,'errmsg':'邮箱验证用户不存在或已注销'})
        else:
            #.激活邮箱
            user.email_active =True
            user.save()
        #构建响应
        return JsonResponse({'code':0,'errmsg':'ok'})



class CreateAddressView(LoginRequiredMixin,View):
    """新地址"""

    def post(self,request):
        """实现新增地址逻辑"""
        #提取参数
        #获取地址个数
        json_dict =json.loads(request.body.decode())

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        #2.校验参数
        #2.1业务性校验——收货地址不能超过20个
        user =request.user
        count =user.addresses.filter(is_deleted =False).count()
        if count>=20:
            return JsonResponse({'code':400,'errmsg':'最多20个地址'})

        #2.2 必要性校验
        if not all([
            receiver,
            province_id,
            city_id,
            district_id,
            place,
            mobile
        ]):
            return JsonResponse({'code':400,'errmsg':'缺少参数'})
        #2.3 约束性校验
        if not re.match(r'^\w{1,20}$',receiver):
            return JsonResponse({'code':400,'errmsg':'收货人昵称有误'})

        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return http.JsonResponse({'code':400,'errmsg':'参数有误'})

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return JsonResponse({'code':400,'errmsg':'参数tel有误'})
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({'code': 400, 'errmsg': '参数email有误'})

        #业务数据数据处理————新建Address对象保存到数据库
        try:
            address =Address.objects.create(
                user =request.user,
                province_id = province_id,
                city_id =city_id,
                district_id =district_id,
                #由于新增地址接口没有传递标题，此处默认把标题设置为receiver
                title =receiver,
                receiver =receiver,
                place =place,
                mobile =mobile,
                tel =tel or '',#tel if tel else
                email =email or ''
            )
            #如果用户的default_address为空，把新增的收货地址
            #设置为用户的默认收货地址
            user = request.user
            if not user.default_address:
                user.default_address = address
                user.save()
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '保存收货地址错误'})

        #4\构建响应
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'address': {
                'id': address.id,
                'title': address.title,
                'receiver': address.receiver,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            }
        })

#展示收货地址——把当前登陆用户的收货地址列表数据返回
class AddressView(LoginRequiredMixin,View):

    def get(self,request):
        #1.提取参数
        user =request.user
        #2\检验参数
        #3.业务数据处理——地址返回
        addresses =user.addresses.filter(is_deleted=False)
        #构建响应参数
        address_list =[]
        for address in addresses:
            #事先组织数据，记录数据
            address_dict ={
                'id': address.id,
                'title': address.title,
                'receiver': address.receiver,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            }
            if address.id == user.default_address.id:
                address_list.insert(0,address_dict)
            else:
                address_list.append(address_dict)

        #构建响应
        return JsonResponse({
            'code':0,
            'errmsg':'ok',
            'default_address_id': user.default_address.id,
            'addresses':address_list
        })


#修改和删除地址
class UpdateDestroyAddressView(View):

    def put(self,request,address_id):
        """修改地址"""
        #接收参数
        user =request
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        # 校验参数
        user = request.user
        count = user.addresses.filter(is_deleted=False).count()
        if count >= 20:
            return JsonResponse({'code': 400, 'errmsg': '最多20个地址'})

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({'code': 400,
                                      'errmsg': '参数mobile有误'})

        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.JsonResponse({'code': 400,
                                          'errmsg': '参数tel有误'})
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({'code': 400,
                                          'errmsg': '参数email有误'})

        # 业务数据处理
        json_dict.pop('province')
        json_dict.pop('city')
        json_dict.pop('district')
        Address.objects.filter(
            pk = address_id
        ).update(**json_dict)
        # update(city="长治市"), city市外键关联字段，必须被赋值为模型类对象.所以此处直接赋值字符串是错误的。

        #构造响应数据
        address = Address.objects.get(id=address_id)
        address_dict={
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        #响应更新地址结果
        return JsonResponse({'code':0,'errmsg':'更新地址成功',
                             'address':address_dict})

    def delete(self,request,address_id):
        user =request.user
        try:
            address = Address.objects.get(pk =address_id,
                                         is_deleted =False)
            address.is_deleted =True
            address.save()

            if address_id == user.default_address.id:
                #如果当前删除的地址刚好是用户的默认地址，把默认地址设置为用户最新增加的地址
                    address =Address.objects.filter(user=user,is_deleted=False).order_by('-update_time')
                    if address:
                        user.default_address =address[0]
                    else:
                        user.default_address =None
                    user.save()

        except Exception as e:
            print(e)
            return JsonResponse({'code':400,'errmsg':'删除地址有误'})

        return JsonResponse({'code':0,'errmsg':'ok'})


#设置默认地址
class DefaultAddressView(LoginRequiredMixin,View):

    def put(self,request,address_id):
        user =request.user

        try:
            address =Address.objects.get(pk =address_id,is_deleted =False)

        except Address.DoesNotExist as e:
            return JsonResponse({'code':400,'errmsg':'地址无线'})

        user.default_address =address
        user.save()

        return JsonResponse({'code':0,'errmsg':'ok'})


#修改地址
class UpdateTitleAddressView(LoginRequiredMixin,View):

    def put(self,request,address_id):
        data =json.loads(request.body.decode())
        title = data.get('title')
        if not title:
            return JsonResponse({'code':400,'errmsg':'缺少参数'})
        if not re.match(r'^\w{1,20}$',title):
            return JsonResponse({'code':400,'errmsg':'title格式错误'})

        try:
            address = Address.objects.get(pk =address_id,is_deleted=False)
        except Address.DoesNotExist as e:
            return JsonResponse({'code':400,'errmsg':'地址无效'})

        address.title =title
        address.save()

        return JsonResponse({'code':0,'errmsg':'ok'})


#修改密码
class ChangePasswordView(LoginRequiredMixin,View):

    def put(self,request):
        data =json.loads(request.body.decode())
        old_password =data.get('old_password')
        new_passwrod =data.get('new_password')
        new_password2 =data.get('new_password2')

        if not all([old_password,new_passwrod,new_password2]):
            return JsonResponse({'code':400,'errmsg':'缺少必要参数'})

        user = request.user
        #检查旧密码
        if not user.check_password(old_password):
            return JsonResponse({'code':400,'errmsg':'旧密码输入错误'})

        if not re.match(r'^[a-zA-Z0-9]{8,20}$',new_passwrod):
            return JsonResponse({'code':400,'errmsg':'新密码格式错误'})
        if new_passwrod != new_password2:
            return JsonResponse({'code':400,'errmsg':'新密码两次输入不一致'})

        if new_passwrod == old_password:
            return JsonResponse({'code':400,'errmsg':'新旧密码不能一致'})

        #修改密码
        user.set_password(new_passwrod)
        user.save()

        #请求用户状态保持
        logout(request)

        response =JsonResponse({'code':0,'errmsg':'ok'})
        response.delete_cookie('username')

        return response

